#include <QByteArray>
#include <QImage>
#include <QBuffer>

#include "gtest/gtest.h"
#include "contentpipe.h"
#include "chanlist.h"
#include "components/chan.h"
#include "components/board.h"
#include "components/attachment.h"
#include "mockobjects/mockchanagent.h"
#include "mockobjects/mockchanmanager.h"
#include "mockobjects/mockboardparser.h"
#include "watermarking/watermarkgeneratorfactory.h"

namespace
{
	using ::testing::_;
	using ::testing::Return;
	using namespace Wishmaster;
	class ContentPipeTest : public ::testing::Test
	{
	protected:
		virtual void SetUp()
		{
			manager = new MockObjects::MockChanManager();
			agent = new MockObjects::MockChanAgent("chan");

			Chan* chan = new Chan("chan", agent, manager);
			manager->chanlist()->addChan(chan);

			parser = new MockObjects::MockBoardParser();
			board = new Board("/b/", chan);
			board->setParser(parser);
			chan->addBoard(board);

			pipe = new ContentPipe(manager);

			new WatermarkGeneratorFactory();
		}

		virtual void TearDown()
		{
			delete WatermarkGeneratorFactory::instance();
			delete pipe;
			delete agent;
			delete manager;
			delete parser;
		}

		ThreadPtr addOneThread(int threadNumericId)
		{
			ThreadPtr thread = ThreadPtr(new Thread(ThreadId("chan", "/b/", threadNumericId), board));
			board->visibleThreads()->push_back(thread);
			return thread;
		}

		PostPtr addPostToThread(ThreadPtr& thread, int postNumId)
		{
			PostPtr post = PostPtr(new Post(PostId(thread->id(), postNumId)));
			thread->appendPost(post);
			return post;
		}

		void addAttachmentToPost(PostPtr& post)
		{
			AttachmentPtr attach = Attachment::create(QUrl("http://chan/thumblink"), QUrl("http://pic.png"));
			post->addAttachment(attach);
		}

		QList<ThreadInfo> makeListWithOneThread(int posts)
		{
			ThreadInfo info;
			info.leaderPicUrl = QUrl("http://chan/threadpic.jpg");
			info.posts = posts;
			info.thread_id = Wishmaster::ThreadId("chan", "/b/", 123456);
			info.timestamp = 0;
			info.topic = "Foo";
			return QList<ThreadInfo>() << info;
		}

		QList<PostInfo> makeListWithOnePostWithOneAttachment(const PostId& id)
		{
			AttachmentInfo attach;
			attach.thumbUrl = QUrl("http://chan/thumb.jpg");
			attach.url = QUrl("http://chan/realthing.jpg");

			PostInfo info;
			info.name = "poster";
			info.post_id = id;
			info.text = "text";
			info.topic = "topic";
			info.attachments.append(attach);
			return QList<PostInfo>() << info;
		}

		void expectOneCallToGetThreadList(int expectedPostCount)
		{
			QList<ThreadInfo> threads = makeListWithOneThread(expectedPostCount);
			EXPECT_CALL(*parser, getThreadList(_, _, _)).WillOnce(Return(threads));
		}

		void expectTwoCallsToGetThreadList(int expectedPostsFirst, int expectedPostsSecond)
		{
			QList<ThreadInfo> threadsBeforeUpdate = makeListWithOneThread(expectedPostsFirst);
			QList<ThreadInfo> threadsAfterUpdate = makeListWithOneThread(expectedPostsSecond);
			EXPECT_CALL(*parser, getThreadList(_, _, _)).WillOnce(Return(threadsBeforeUpdate))
					.WillOnce(Return(threadsAfterUpdate));
		}

		void setExpectationsAndIssueFirstCallTo_contentBoardIndex(int expectedPostsFirst,
																  int expectedPostsSecond)
		{
			expectTwoCallsToGetThreadList(expectedPostsFirst, expectedPostsSecond);
			pipe->contentBoardIndex("chan", "/b/", QByteArray());
			ASSERT_TRUE(board->visibleThreads()->count() != 0);
		}

		void emitContentBoardIndex()
		{
			pipe->contentBoardIndex("chan", "/b/", QByteArray());
		}

		QImage createTestImage()
		{
			QImage img(10, 10, QImage::Format_RGB32);
			img.fill(0xffffffff); // white color
			return img;
		}

		QByteArray getImageData(const QImage& img)
		{
			QByteArray arr;
			QBuffer buf(&arr);
			img.save(&buf, "PNG");
			return arr;
		}

		MockObjects::MockBoardParser* parser;
		ContentPipe* pipe;
		ChanAgent* agent;
		Board* board;
		MockObjects::MockChanManager* manager;
	};

	TEST_F(ContentPipeTest, contentBoardIndex_NoThreads)
	{
		QList<ThreadInfo> threads;
		EXPECT_CALL(*parser, getThreadList(_, _, _)).WillOnce(Return(threads));

		emitContentBoardIndex();

		ASSERT_EQ(0, board->visibleThreads()->count());
	}

	TEST_F(ContentPipeTest, contentBoardIndex_OneThread)
	{
		expectOneCallToGetThreadList(12);

		emitContentBoardIndex();

		ASSERT_EQ(1, board->visibleThreads()->count());
	}

	TEST_F(ContentPipeTest, contentBoardIndex_requestsThreadOpPicThumbnail)
	{
		expectOneCallToGetThreadList(12);
		EXPECT_CALL(*manager, requestThumbnail(_, Attachment::NoAttachment, _));

		emitContentBoardIndex();
	}

	TEST_F(ContentPipeTest, contentBoardIndex_OneThread_ThreadPostCountUpdated)
	{
		expectOneCallToGetThreadList(12);

		emitContentBoardIndex();

		ThreadPtr thread = board->visibleThreads()->getNth(0);
		ASSERT_EQ(12, thread->getPostsCount());
	}

	TEST_F(ContentPipeTest, contentBoardIndex_ThreadWithNewMessages_ChangeState)
	{
		setExpectationsAndIssueFirstCallTo_contentBoardIndex(12, 24);
		ThreadPtr thread = board->visibleThreads()->getNth(0);
		thread->setState(Thread::sUpToDate);

		emitContentBoardIndex();

		ASSERT_TRUE(thread->state() == Thread::sNewPosts);
	}

	TEST_F(ContentPipeTest, contentBoardIndex_ThreadWithoutNewMessages_RetainState)
	{
		setExpectationsAndIssueFirstCallTo_contentBoardIndex(12, 12);
		ThreadPtr thread = board->visibleThreads()->getNth(0);
		thread->setState(Thread::sUpToDate);

		emitContentBoardIndex();

		ASSERT_TRUE(thread->state() == Thread::sUpToDate);
	}

	TEST_F(ContentPipeTest, contentThread_OnePost)
	{
		ThreadPtr thread = addOneThread(123456);
		QList<PostInfo> posts = makeListWithOnePostWithOneAttachment(PostId(thread->id(), 123456));
		EXPECT_CALL(*parser, getPosts(thread->id(), _)).WillOnce(Return(posts));

		pipe->contentThread(QByteArray(), thread->id());

		ASSERT_EQ(1, thread->getPostsCount());
		ASSERT_EQ(1, thread->getPost(0)->attachmentCount());
	}

	TEST_F(ContentPipeTest, contentThread_NotifiesChanManager)
	{
		ThreadPtr thread = addOneThread(123456);
		QList<PostInfo> posts = makeListWithOnePostWithOneAttachment(PostId(thread->id(), 123456));
		EXPECT_CALL(*parser, getPosts(thread->id(), _)).WillOnce(Return(posts));
		EXPECT_CALL(*manager, updateContent(PostId(thread->id(), 0), Attachment::NoAttachment));

		pipe->contentThread(QByteArray(), thread->id());
	}

	TEST_F(ContentPipeTest, contentThread_OnePostWithReply)
	{
		ThreadPtr thread = addOneThread(123456);
		PostId replyId = PostId(thread->id(), 1234567);
		QList<PostInfo> posts = makeListWithOnePostWithOneAttachment(PostId(thread->id(), 123456));
		posts[0].replies.insert(replyId);
		EXPECT_CALL(*parser, getPosts(thread->id(), _)).WillOnce(Return(posts));

		pipe->contentThread(QByteArray(), thread->id());

		ASSERT_TRUE(thread->getPost(0)->replies().contains(replyId));
	}

	TEST_F(ContentPipeTest, contentThumbnail)
	{
		// Slow test
		int postNumId = 123457;
		int attachmentIndex = 0;
		ThreadPtr thread = addOneThread(123456);
		PostPtr post = addPostToThread(thread, postNumId);
		QImage testImage = createTestImage();
		QByteArray testImageData = getImageData(testImage);
		addAttachmentToPost(post);

		pipe->contentThumbnail(testImageData, PostId(thread->id(), postNumId), attachmentIndex);

		ASSERT_TRUE(post->attachment(attachmentIndex)->thumbnail() == testImage);
	}

	TEST_F(ContentPipeTest, contentAttachment)
	{
		int postNumId = 123457;
		int attachmentIndex = 0;
		ThreadPtr thread = addOneThread(123456);
		PostPtr post = addPostToThread(thread, postNumId);
		QByteArray testAttachmentData = QByteArray(64, 0x12);
		addAttachmentToPost(post);

		pipe->contentAttachment(testAttachmentData, PostId(thread->id(), postNumId), 0, "text/html");

		ASSERT_TRUE(post->attachment(attachmentIndex)->data() == testAttachmentData);
	}
}
